/*
 * Flashlib - QB9 common Flash library
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package com.qb9.flashlib.audio
{
	import com.qb9.flashlib.utils.ArrayUtil;
	
	import flash.events.Event;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;

	/**
	 * Provides a simple API for the multiple operations perfomed on sounds.
	 * 
	 * @see flash.media.Sound
	 * @see flash.media.SoundChannel
	 * @see flash.media.SoundTransform
	 */		
	public class MultiChanneledAudio extends Playable implements IPlayable
	{
		protected var channels:Array;
		protected var transform:SoundTransform;
		protected var sound:Sound;
		protected var maxChannels:uint;
		protected var lastVol:Number;		
		
		public function MultiChanneledAudio(sound:Sound, maxChannels:uint = uint.MAX_VALUE, id:String = null)
		{
			super(id);
			
			this.sound = sound;
			this.maxChannels = maxChannels;
			this.transform = new SoundTransform();
			this.channels = new Array();
		}
		
		protected override function _onPlayComplete(e:Event) : void
		{
			super._onPlayComplete(e);
			e.target.removeEventListener(Event.SOUND_COMPLETE, _onPlayComplete);
			ArrayUtil.removeElement(channels, e.target);			
			
			if (!channels.length)
				super.stop();
		}
		
		protected function _forEach(fn:Function) : void
		{
			for (var i:int = channels.length - 1; i >= 0; i--)
				fn(channels[i], i);
		}
		
		/** @inheritDoc */
		public override function get length():Number
		{
			return sound.length;
		}

		/** @inheritDoc */
		public override function get position():Number
		{
			return channels.length ? channels[0].position : 0;
		}
		
		/** @inheritDoc */
		public override function get volume():Number
		{
			return transform.volume;
		}

		/** @inheritDoc */
		public override function set volume(vol:Number):void
		{
			super.volume = vol;
			transform.volume = vol;
			_forEach(_updateVolume);
		}
		
		protected function _updateVolume(channel:SoundChannel, i:int) : void
		{
			channel.soundTransform = transform;
		}
		
		/** @inheritDoc */
		public override function get playing():Boolean
		{
			return !!channels.length;
		}

		protected function playChannel(pos:uint = 0):SoundChannel
		{
			var channel:SoundChannel = sound.play(pos, loops, transform);
			if (!channel) // Flash provides a limited amount of channels
				return null;
				
			channel.addEventListener(Event.SOUND_COMPLETE, _onPlayComplete);
			return channel;
		}

		protected function stopChannel(channel:SoundChannel, i:uint):void
		{
			pauseChannel(channel, i);
			ArrayUtil.removeElement(channels, channel);
		}

		protected function pauseChannel(channel:SoundChannel, i:uint):void
		{
			channel.stop();
			if (channel.hasEventListener(Event.SOUND_COMPLETE))
				channel.removeEventListener(Event.SOUND_COMPLETE, _onPlayComplete);
		}

		protected function resumeChannel(channel:SoundChannel, i:uint):void
		{
			pauseChannel(channel, i);
			var channel:SoundChannel = playChannel(channel.position);
			if (channel)
				channels[i] = channel;
		}

		/**
		 * Pause the playback of all the available channels.
		 */
		public override function pause():void
		{
			super.pause();
			_forEach(pauseChannel);
		}
		
		/**
		 * Resume the playback of all the available channels.
		 */
		public override function resume():void
		{
			super.resume();
			_forEach(resumeChannel);
		}

		/**
		 * Stop the playback of all available channels.
		 */
		public override function stop():void
		{
			super.stop();
			_forEach(stopChannel);
		}
		
		/** @inheritDoc */
		public override function loop(times:int=int.MAX_VALUE):void
		{
			super.loop(times);

			if (channels.length == maxChannels)
				stopChannel(channels[0], 0);

			var channel:SoundChannel = playChannel( times === 1 ? 0 : 60 );
			if (channel)
				channels.push(channel);
		}

		/** @inheritDoc */
		public override function dispose():void
		{
			stop();
			sound = null;
			transform = null;
		}
	}
}
